package com.es.www.kyl.service;

import com.es.www.kyl.config.property.JwtProperty;
import com.es.www.kyl.domain.User;
import com.es.www.kyl.exception.ResException;
import com.es.www.kyl.mapper.UserMapper;
import com.es.www.kyl.util.RedisUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static com.es.www.kyl.constant.BusinessResultEnum.TOKEN_NOT_EXIST;
import static com.es.www.kyl.constant.BusinessResultEnum.USER_PERMISSION_DENIED;
import static com.es.www.kyl.constant.RedisConstants.DENY_TOKEN_KEY_PREFIX;

/**
 * @author Estranger
 * @date 2025/7/30
 * description 
 */
@Service
public class JwtService {

	@Autowired
	private JwtProperty jwtProperty;
	@Autowired
	private RedisUtils redisUtils;
	@Autowired
	private UserMapper userMapper;

	private SecretKey getSecretKey() {
		return Keys.hmacShaKeyFor(jwtProperty.getSecret().getBytes());
	}

	private SecretKey getSigningKey() {
		return Keys.hmacShaKeyFor(jwtProperty.getSecret().getBytes(StandardCharsets.UTF_8));
	}

	public String generateAccessToken(Integer userId) {
		return generateToken(userId, jwtProperty.getExpiration());
	}

	public String generateRefreshToken(Integer userId) {
		return generateToken(userId, jwtProperty.getRefreshExpiration());
	}


	private String generateToken(Integer userId, long expirationTime) {
		long nowMillis = System.currentTimeMillis();
		//有效期
		long expirationMillis = nowMillis + expirationTime;
		// 为每个新Token生成一个唯一的JTI
		String jti = UUID.randomUUID().toString();

		return Jwts.builder()
				.setId(jti)
				.setSubject(String.valueOf(userId))
				.setIssuedAt(new Date(nowMillis))
				.setExpiration(new Date(expirationMillis))
				.signWith(getSecretKey(), SignatureAlgorithm.HS256)
				.compact();
	}

	/**
	 * 用于验证和解析Token
 	 */
	private Claims extractAllClaims(String token) {
		Claims claims;
		try {
			claims = Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
		}catch (Exception e) {
			// 如果解析失败，可能是Token已过期或无效
			throw new ResException(TOKEN_NOT_EXIST);
		}
		return claims;
	}

	public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
		final Claims claims = extractAllClaims(token);
		return claimsResolver.apply(claims);
	}

	public String extractUserId(String token) {
		return extractClaim(token, Claims::getSubject);
	}

	public Date extractExpiration(String token) {
		return extractClaim(token, Claims::getExpiration);
	}

	public boolean isTokenExpired(String token) {
		return extractExpiration(token).before(new Date());
	}

	public boolean validateToken(String token, User user) {
		final String userId = extractUserId(token);
		return (userId.equals(user.getId().toString()) && !isTokenExpired(token));
	}

	/**
	 * 将JWT加入黑名单
	 */
	public void doValidateToken(String token) {
		boolean tokenExpired = isTokenExpired(token);
		if (!tokenExpired) {
			// 如果Token未过期，将其加入黑名单
			String jtiId = extractClaim(token, Claims::getId);
			Date expiration = extractExpiration(token);
			long remainingMillis = expiration.getTime() - System.currentTimeMillis();
			redisUtils.setStringValue(DENY_TOKEN_KEY_PREFIX + jtiId, token, remainingMillis, TimeUnit.MILLISECONDS);
		}
	}

	/**
	 * 验证JWT有效性并检查黑名单
	 *
	 * @param token
	 */
	public void isTokenValid(String token) {
		String jtiId = extractClaim(token, Claims::getId);
		String redisKey = DENY_TOKEN_KEY_PREFIX + jtiId;
		//在黑名单中，说明已经被注销
		if(redisUtils.hasStringKey(redisKey)){
			throw new ResException(TOKEN_NOT_EXIST);
		}
	}
}
